import datetime
import Core.Gadget as Gadget

class Calender(object):
    def __init__(self, database, calenderName="SH"):
        #
        self.database = database

        #
        self.country = "CN"
        if calenderName == "SH":
            self.country = "CN"

        #
        datetimeNow = datetime.datetime.now()
        datetimeNow = Gadget.ToDate(datetimeNow)
        self.tradingDays = []
        self.datetime1 = datetimeNow + datetime.timedelta(days=-30)
        self.datetime2 = datetimeNow + datetime.timedelta(days=30)
        self.tradingDays = self.GetTradingDays(self.datetime1, self.datetime2)

    def GetTradingDays(self, datetime1, datetiem2):

        rets = []
        stdDateTime1 = Gadget.ToUTCDateTime(datetime1)
        stdDateTime2 = Gadget.ToUTCDateTime(datetiem2)

        midDateTime = datetime.datetime(2018,10,1)
        stdMidDateTime = Gadget.ToUTCDateTime(midDateTime)

        # ---已经预存---
        if stdDateTime2 <= stdMidDateTime:
            tmpDatetiem = stdDateTime2
        else:
            tmpDatetiem = stdMidDateTime

        tradings = self.database.find("Misc", "TradingDays", beginDateTime=stdDateTime1, endDateTime=tmpDatetiem, query={"Country": self.country})
        for day in tradings:
            rets.append(Gadget.ParseDateTime(day["DateTime"]))

        # ---剩下的计算---
        if stdDateTime1 > stdMidDateTime:
            stdTmpDatetiem = stdDateTime1
            tmpDatetiem = datetime1
        else:
            stdTmpDatetiem = stdMidDateTime
            tmpDatetiem = midDateTime

        holidaysList = []
        holidays = self.database.find("Misc", "Holidays", beginDateTime=stdTmpDatetiem, endDateTime=stdDateTime2, query={"Country":self.country, "Type": "Holiday"})
        for day in holidays:
            holidaysList.append(day["DateTime"])

        # ---极其个别的情况下，周末需要补休且交易---
        tradinglist = []
        tradings = self.database.find("Misc", "Holidays", beginDateTime=stdTmpDatetiem, endDateTime=stdDateTime2, query={"Country": self.country, "Type": "Tradingday"})
        for day in tradings:
            tradinglist.append(day["DateTime"])

        currentDateTime = tmpDatetiem
        while currentDateTime <= datetiem2:
            strDateTime = Gadget.ToDateTimeString(currentDateTime)
            isTradingDay = True
            #
            if currentDateTime.weekday() in range(5):
                if strDateTime in holidaysList:
                    isTradingDay = False
            else: # weekend
                isTradingDay = False
                if strDateTime in tradinglist:
                    isTradingDay = True

            # ---Add to Result---
            if isTradingDay:
                rets.append(currentDateTime)

            # ---Plus day---
            currentDateTime += datetime.timedelta(days=1)

        return rets

    def GetNextTradingDays(self, datetime2):
        #
        day = Gadget.ToDate(datetime2)

        # ---update trading days cache---
        lastDay = self.tradingDays[len(self.tradingDays)-1]
        lastDay = Gadget.ToDate(lastDay)
        if day >= lastDay:
            self.datetime2 += datetime.timedelta(days=30)
            self.tradingDays = self.GetTradingDays(self.datetime1, self.datetime2)
            nextTradingDay, index = self.FindInList(self.tradingDays, day, "Next")
        else:
            nextTradingDay, index = self.FindInList(self.tradingDays, day, "Next")

        return nextTradingDay


    def FindInList(self, list, target, option="Previous"):

        beginIndex = 0
        endIndex = len(list)-1
        index = self.GetIndex(list, target, beginIndex, endIndex, 0, option)
        value = list[index]
        return value, index


    def GetIndex(self, list, target, beginIndex, endIndex, recursive, option):
        length = endIndex - beginIndex + 1
        beginValue = list[beginIndex]
        endValue = list[endIndex]

        # -------------------------------
        # ---Special Case---
        if target < beginValue:
            if option == "Previous":
                return beginIndex - 1
            if option == "Next":
                return beginIndex
            if option == "Exact":
                return -1
        if target > endValue:
            if option == "Previous":
                return endIndex
            if option == "Next":
                return endIndex + 1
            if option == "Exact":
                return -1

        #
        if length == 2:
            exactIndex = -1
            if target == beginValue:
                exactIndex = beginIndex
            elif target == endValue:
                exactIndex = endIndex

            if option == "Exact":
                return exactIndex
            if option == "Previous":
                return exactIndex -1
            if option == "Next":
                return exactIndex + 1
            return -1

        else: # length == 1
            midIndex = beginIndex + int((length * 0.5))
            tmpValue = list[midIndex]
            if target <= tmpValue:
                return self.GetIndex(list, target, beginIndex, midIndex, recursive + 1, option)
            else:
                return self.GetIndex(list, target, midIndex, endIndex, recursive + 1, option)

        return -1


    def IsWorkingDay(datetime1):
        pass

    def IsHoliday(datetime1):
        pass

    def IsTradingDay(datetime1):
        pass

# ---Type:Holiday---
# ---Type:Workingday : 補休---
# ---Type:Tradingday : 補休未必是交易日---
def AddHolidays(database, holidays, country="CN", type="Holiday"):
    #
    for dt in holidays:
        doc = {}
        doc["DateTime"] = Gadget.ToDateTimeString(dt)
        doc["StdDateTime"] = Gadget.ToUTCDateTime(dt)
        doc["Country"] = country
        doc["Type"] = type
        doc["Key"] = country + "_" + type + "_" + doc["DateTime"]

        database.upsert("Misc","Holidays", {"Key":doc["Key"]}, doc)


def AddTradingDays(database, holidays, country="CN"):
    #
    for dt in holidays:
        doc = {}
        doc["DateTime"] = Gadget.ToDateTimeString(dt)
        doc["StdDateTime"] = Gadget.ToUTCDateTime(dt)
        doc["Country"] = country
        doc["Key"] = country + "_" + doc["DateTime"]

        database.upsert("Misc", "TradingDays", {"Key":doc["Key"]}, doc)



